#include<iostream>
#include<graphics.h>
#include<conio.h>
#include<cmath>
#include <windows.h>
#include<random>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#define MOVE1_SOUND_ALIAS "move1_effect"
#define OPENING_SOUND_ALIAS "opening_effect"
#define MOVE11_SOUND_ALIAS "move11_effect"
#define MOVE22_SOUND_ALIAS "move22_effect"
#define MOVE33_SOUND_ALIAS "move33_effect"
#define MOVE44_SOUND_ALIAS "move44_effect"
#define MOVE55_SOUND_ALIAS "move55_effect"
#define MOVE66_SOUND_ALIAS "move66_effect"
#define MOVE77_SOUND_ALIAS "move77_effect"
#define OP_SOUND_ALIAS "op_effect"
#define ED_SOUND_ALIAS "ed_effect"
using namespace std;
int sum = 1;//Saki酱第n声
const int blocks = 4;
const int blocks_width = 200;
const int  devi = 30;
COLORREF tileColors[] = {
	RGB(205, 193, 180),  // 0
	RGB(238, 228, 218),  // 2
	RGB(237, 224, 200),  // 4
	RGB(242, 177, 121),  // 8
	RGB(245, 149, 99),   // 16
	RGB(246, 124, 95),   // 32
	RGB(246, 94, 59),    // 64
	RGB(242, 177, 121),  // 128
	RGB(237, 204, 97),   // 256
	RGB(255, 0, 128),    // 512
	RGB(145, 0, 72),     // 1024
	RGB(242,17,158)   // 2048
};
IMAGE tileImages[12];
void LoadTileImages() {
	for (int i = 1; i < 12; i++) {
		int value = 1 << i;
		wchar_t filename[256];
		swprintf(filename, 256, L"res/%d.png", value);
		loadimage(&tileImages[i], filename, blocks_width, blocks_width);
	}
}
int num[blocks][blocks];
POINT posi[blocks][blocks];
bool f = 1;
bool flag = 0;
void LoadSounds() {
	mciSendString(_T("open res/output.wav alias ") _T(MOVE1_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/opening.wav alias ") _T(OPENING_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/11.wav alias ") _T(MOVE11_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/22.wav alias ") _T(MOVE22_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/33.wav alias ") _T(MOVE33_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/44.wav alias ") _T(MOVE44_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/55.wav alias ") _T(MOVE55_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/66.wav alias ") _T(MOVE66_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/77.wav alias ") _T(MOVE77_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/op.wav alias ") _T(OP_SOUND_ALIAS), NULL, 0, NULL);
	mciSendString(_T("open res/ed.wav alias ") _T(ED_SOUND_ALIAS), NULL, 0, NULL);
}
void PlayMoveSound1() {

	static int instance = 0;
	TCHAR cmd[256];
	_stprintf(cmd, _T("open res/output.wav alias %s%d"), _T(MOVE1_SOUND_ALIAS), ++instance % 5);
	mciSendString(cmd, NULL, 0, NULL);
	_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE1_SOUND_ALIAS), instance % 5);
	mciSendString(cmd, NULL, 0, NULL);
}
void PlayMoveSound2()
{
	switch (sum%7+1)
	{
		case 1:
		{
			static int instance = 0;
			TCHAR cmd[256];
			_stprintf(cmd, _T("open res/11.wav alias %s%d"), _T(MOVE11_SOUND_ALIAS), ++instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE11_SOUND_ALIAS), instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			break;
		}
		case 2:
		{
			static int instance = 0;
			TCHAR cmd[256];
			_stprintf(cmd, _T("open res/22.wav alias %s%d"), _T(MOVE22_SOUND_ALIAS), ++instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE22_SOUND_ALIAS), instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			break;
		}
		case 3:
		{
			static int instance = 0;
			TCHAR cmd[256];
			_stprintf(cmd, _T("open res/33.wav alias %s%d"), _T(MOVE33_SOUND_ALIAS), ++instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE33_SOUND_ALIAS), instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			break;
		}
		case 4:
		{
			static int instance = 0;
			TCHAR cmd[256];
			_stprintf(cmd, _T("open res/44.wav alias %s%d"), _T(MOVE44_SOUND_ALIAS), ++instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE44_SOUND_ALIAS), instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			break;
		}
		case 5:
		{
			static int instance = 0;
			TCHAR cmd[256];
			_stprintf(cmd, _T("open res/55.wav alias %s%d"), _T(MOVE55_SOUND_ALIAS), ++instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE55_SOUND_ALIAS), instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			break;
		}
		case 6:
		{
			static int instance = 0;
			TCHAR cmd[256];
			_stprintf(cmd, _T("open res/66.wav alias %s%d"), _T(MOVE66_SOUND_ALIAS), ++instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE66_SOUND_ALIAS), instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			break;
		}
		case 7:
		{
			static int instance = 0;
			TCHAR cmd[256];
			_stprintf(cmd, _T("open res/77.wav alias %s%d"), _T(MOVE77_SOUND_ALIAS), ++instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			_stprintf(cmd, _T("play %s%d from 0"), _T(MOVE77_SOUND_ALIAS), instance % 5);
			mciSendString(cmd, NULL, 0, NULL);
			break;
		}
	}
	sum++;
}
void Gamedraw()
{
	initgraph(blocks * blocks_width + 5 * devi, blocks * blocks_width + 5 * devi);
	setbkcolor(RGB(255, 255, 255));
	cleardevice();
	settextstyle(100, 0, _T("宋体"));
	settextcolor(RGB(30, 30, 30));
	outtextxy(215, 175, _T("正常版2048"));
	setbkmode(TRANSPARENT);
	settextstyle(40, 0, _T("宋体"));
	outtextxy(245, 375, _T("请按[空格键]以开始游戏"));
	settextstyle(30, 0, _T("宋体"));
	outtextxy(170, 500, _T("操作指南：“↑、↓、←、→”以上下左右移动"));
	outtextxy(355, 650, _T("[esc]退出游戏"));
}
bool isGameOver() {
	for (int i = 0; i < blocks; i++) {
		for (int j = 0; j < blocks; j++) {
			if (num[i][j] == 0) return false;
		}
	}
	for (int i = 0; i < blocks; i++) {
		for (int j = 0; j < blocks; j++) {
			if (j < blocks - 1 && num[i][j] == num[i][j + 1]) {
				return false;
			}
			if (i < blocks - 1 && num[i][j] == num[i + 1][j]) {
				return false;
			}
		}
	}
	return true;
}
void Gameoverfail()
{
	if(!flag)	
		MessageBox(GetHWnd(), _T("Game Over!"), _T("2048"), MB_OK);
	else
	{
		MessageBox(GetHWnd(), _T("Game Over!"), _T("2048"), MB_OK);
		initgraph(blocks * blocks_width + 5 * devi, blocks * blocks_width + 5 * devi);
		IMAGE img;
		loadimage(&img, _T("res/ed1.png"));
		cleardevice();
		putimage(0, 0, &img);
		mciSendString(_T("play ") _T(MOVE77_SOUND_ALIAS) _T(" from 0"), NULL, 0, NULL);
		getchar();
	}
}
void Gameoverwin()
{
	for (int i = 0; i < blocks; i++)
	{
		for (int j = 0; j < blocks; j++)
		{
			if (num[i][j] == 0) {
				setfillcolor(tileColors[0]);
			}
			else {
				int index = (int)(log2(num[i][j]));
				setfillcolor(tileColors[index]);
			}
			solidrectangle(posi[i][j].x, posi[i][j].y, posi[i][j].x + blocks_width, posi[i][j].y + blocks_width);
			if (num[i][j])
			{
				wchar_t nu[5] = L" ";
				settextstyle(80, 0, _T("宋体"));
				settextcolor(RGB(30, 30, 30));
				swprintf(nu, 5, L"%d", num[i][j]);
				setbkmode(TRANSPARENT);
				int textWidth = textwidth(nu);
				int textHeight = textheight(nu);
				int centerX = posi[i][j].x + (blocks_width - textWidth) / 2;
				int centerY = posi[i][j].y + (blocks_width - textHeight) / 2;
				outtextxy(centerX, centerY, nu);
			}
		}
	}
	if (!flag)
	{
		MessageBox(GetHWnd(), _T("Congratulations!"), _T("2048"), MB_OK);
		MessageBox(GetHWnd(), _T("恭喜你解锁隐藏版！在主界面摁[Enter]试试吧！"), _T("2048"), MB_OK);
	}
	else
	{
		initgraph(blocks * blocks_width + 5 * devi, blocks * blocks_width + 5 * devi);
		IMAGE img;
		loadimage(&img, _T("res/ed.png"));
		cleardevice();
		putimage(0, 0, &img);
		mciSendString(_T("play ") _T(ED_SOUND_ALIAS) _T(" from 0"), NULL, 0, NULL);
		getchar();
	}
}
int random()
{
	if (rand() % 5 == 0)
	{
		return 4;
	}
	else
		return 2;
}
void initnum()
{
	
	while (1)
	{
		int x = rand() % blocks;
		int y = rand() % blocks;
		if (num[x][y]==0)
		{
			num[x][y] = random();
			break;
		}
		
	}
}
void initposi()
{
	for(int i=0;i<blocks;i++)
		for (int j = 0; j < blocks; j++)
		{
			posi[i][j].x = j * blocks_width + (j + 1) * devi;
			posi[i][j].y = i * blocks_width + (i + 1) * devi;
		}
}
void move_up()
{
	f=0;//判断操作是否有效
	for (int i = 0; i < blocks; i++)
	{
		int index = 0;
		for (int j = 1; j < blocks; j++)
		{
			if (num[j][i] != 0)
			{
				if (num[index][i] == 0)
				{
					num[index][i] = num[j][i];
					num[j][i] = 0;
					f = 1;
				}
				else
				{
					if (num[index][i] == num[j][i])
					{
						num[index][i] *= 2;
						num[j][i] = 0;
						f = 1;		
					}
					else
					{
						if (index + 1 < blocks && index + 1 != j) 
						{
							num[index + 1][i] = num[j][i];
							num[j][i] = 0;
							f = 1;							
						}
						index++;
					}
				}
				if (num[index][i] == 2048)
				{
					Gameoverwin();
				}				
			}
		}
	}
	if (f)
	{
		if (!flag)
			PlayMoveSound1();
		else
			PlayMoveSound2();
		initnum();
		if (isGameOver())
		{
			Gameoverfail();
		}
	}
}
void move_down()
{
	f = 0;//判断操作是否有效
	for (int i = 0; i < blocks; i++)
	{
		int index = blocks - 1;
		for (int j = index - 1; j >= 0; j--)
		{
			if (num[j][i] != 0)
			{
				if (num[index][i] == 0)
				{
					num[index][i] = num[j][i];
					num[j][i] = 0;
					f = 1;
				}
				else
				{
					if (num[index][i] == num[j][i])
					{
						num[index][i] *= 2;
						num[j][i] = 0;
						f = 1;
					}
					else
					{
						if (index - 1 >= 0 && index - 1 != j)
						{
							num[index - 1][i] = num[j][i];
							num[j][i] = 0;
							f = 1;
						}
						index--;
					}
				}
				if (num[index][i] == 2048)
				{
					Gameoverwin();
				}
			}
		}
	}
	if (f)
	{
		if (!flag)
			PlayMoveSound1();
		else
			PlayMoveSound2();
		initnum();
		if (isGameOver())
		{
			Gameoverfail();
		}
	}
}
void move_left()
{
	f = 0;//判断操作是否有效
	for (int j = 0; j < blocks; j++)
	{
		int index = 0;
		for (int i = 1; i < blocks; i++)
		{
			if (num[j][i] != 0)
			{
				if (num[j][index] == 0)
				{
					num[j][index] = num[j][i];
					num[j][i] = 0;
					f = 1;
				}
				else
				{
					if (num[j][index] == num[j][i])
					{
						num[j][index] *= 2;
						num[j][i] = 0;
						f = 1;
					}
					else
					{
						if (index + 1 < blocks && index + 1 != i)
						{
							num[j][index + 1] = num[j][i];
							num[j][i] = 0;
							f = 1;
						}
						index++;
					}
				}
				if (num[j][index] == 2048)
				{
					Gameoverwin();
				}
			}
		}
	}
	if (f)
	{
		if (!flag)
			PlayMoveSound1();
		else
			PlayMoveSound2();
		initnum();
		if (isGameOver())
		{
			Gameoverfail();
		}
	}

}
void move_right()
{
	f = 0;//判断操作是否有效
	for (int j = 0; j < blocks; j++)
	{
		int index = blocks - 1;
		for (int i = index - 1; i >= 0; i--)
		{
			if (num[j][i] != 0)
			{
				if (num[j][index] == 0)
				{
					num[j][index] = num[j][i];
					num[j][i] = 0;
					f = 1;
				}
				else
				{
					if (num[j][index] == num[j][i])
					{
						num[j][index] *= 2;
						num[j][i] = 0;
						f = 1;
					}
					else
					{
						if (index - 1 >= 0 && index - 1 != i)
						{
							num[j][index - 1] = num[j][i];
							num[j][i] = 0;
							f = 1;
						}
						index--;
					}
				}
				if (num[j][index] == 2048)
				{
					Gameoverwin();
				}
			}
		}
	}
	if (f)
	{
		if (!flag)
			PlayMoveSound1();
		else
			PlayMoveSound2();
		initnum();
		if (isGameOver())
		{
			Gameoverfail();
		}
	}
}
void Gamestart1()	
{
	initgraph(blocks * blocks_width + 5 * devi, blocks * blocks_width + 5 * devi);
	setbkcolor(RGB(187, 173, 160));
	cleardevice();
	initnum();
	initnum();	
	while (1)
	{
		if(f)
			for (int i = 0; i < blocks; i++)
			{
				for (int j = 0; j < blocks; j++)
				{
					if (num[i][j] == 0) {
						setfillcolor(tileColors[0]);
					}
					else {
						int index = (int)(log2(num[i][j]));
						setfillcolor(tileColors[index]);
					}
					solidrectangle(posi[i][j].x, posi[i][j].y, posi[i][j].x + blocks_width, posi[i][j].y + blocks_width);
					if (num[i][j])
					{
						wchar_t nu[5] = L" ";
						settextstyle(80, 0, _T("宋体"));
						settextcolor(RGB(30, 30, 30));
						swprintf(nu, 5, L"%d", num[i][j]);
						setbkmode(TRANSPARENT);
						int textWidth = textwidth(nu);
						int textHeight = textheight(nu);
						int centerX = posi[i][j].x + (blocks_width - textWidth) / 2;
						int centerY = posi[i][j].y + (blocks_width - textHeight) / 2;
						outtextxy(centerX, centerY, nu);
					}
				}
			}
		ExMessage msg;
		if (peekmessage(&msg,EM_KEY))
		{
			getmessage(&msg, EM_KEY);
			switch (msg.vkcode)
			{
				case VK_UP:	move_up(); break;
				case VK_DOWN:	move_down(); break;					
				case VK_LEFT:	move_left(); break;					
				case VK_RIGHT:	move_right(); break;
			}
		}
		FlushBatchDraw();
		Sleep(10);
	}
}
void Gamestart2()
{
	mciSendString(_T("play ") _T(OP_SOUND_ALIAS) _T(" from 0"), NULL, 0, NULL);
	initgraph(blocks * blocks_width + 5 * devi, blocks * blocks_width + 5 * devi);
	setbkcolor(RGB(187, 173, 160));
	cleardevice();
	LoadTileImages(); 
	initnum();
	initnum();
	while (1)
	{
		if (f)
			for (int i = 0; i < blocks; i++)
			{
				for (int j = 0; j < blocks; j++)
				{
					if (num[i][j] == 0) {
						setfillcolor(tileColors[0]);
						solidrectangle(posi[i][j].x, posi[i][j].y, posi[i][j].x + blocks_width, posi[i][j].y + blocks_width);
					}
					else 
						if (num[i][j])
						{
							int t = log2(num[i][j]);
							putimage(posi[i][j].x, posi[i][j].y, &tileImages[t]);						
						}
				}
			}
		ExMessage msg;
		if (peekmessage(&msg, EM_KEY))
		{
			getmessage(&msg, EM_KEY);
			switch (msg.vkcode)
			{
			case VK_UP:	move_up(); break;
			case VK_DOWN:	move_down(); break;
			case VK_LEFT:	move_left(); break;
			case VK_RIGHT:	move_right(); break;
			}
		}
		FlushBatchDraw();
		Sleep(10);
	}
}
void Gamedrawspecial()
{
	flag = 1;
	initgraph(blocks * blocks_width + 5 * devi, blocks * blocks_width + 5 * devi);
	setbkcolor(RGB(255, 255, 255));
	setbkmode(TRANSPARENT);
	mciSendString(_T("play ") _T(OPENING_SOUND_ALIAS) _T(" from 0"), NULL, 0, NULL);
	IMAGE img;
	loadimage(&img, _T("res/0.png"));
	cleardevice();
	putimage(290, 40, &img);
	settextstyle(70, 0, _T("宋体"));
	settextcolor(RGB(30, 30, 30));
	outtextxy(190, 200, _T("合成大祥子之2048"));
	setbkmode(TRANSPARENT);
	settextstyle(40, 0, _T("宋体"));
	outtextxy(245, 375, _T("请按[空格键]以开始游戏"));
	settextstyle(30, 0, _T("宋体"));
	outtextxy(170, 500, _T("操作指南：“↑、↓、←、→”以上下左右移动"));
	outtextxy(355, 650, _T("[esc]退出游戏"));
	ExMessage msg;
	while (true)
	{
		if (peekmessage(&msg, EM_KEY))
		{
			switch (msg.vkcode)
			{
			case VK_SPACE: Gamestart2(); break;
			case VK_ESCAPE: closegraph(); exit(0) ;
			}
		}
		FlushBatchDraw();
		Sleep(10);
	}
}
int main()
{
	LoadSounds();
	initposi();
	srand(time(0));
	Gamedraw();
	ExMessage msg;
	while (true)
	{
		if (peekmessage(&msg, EM_KEY))
		{
			switch (msg.vkcode)
			{
			case VK_SPACE: Gamestart1(); break;
			case VK_RETURN: Gamedrawspecial(); break;
			case VK_ESCAPE: closegraph(); return 0;
			}
		}
		FlushBatchDraw();
		Sleep(10);
	}
	getchar();
	return 0;
}
/*
测试样例(失败）
num[0][0] = 2; num[0][1] = 4; num[0][2] = 8; num[0][3] = 16;
num[1][0] = 4; num[1][1] = 8; num[1][2] = 16; num[1][3] = 32;
num[2][0] = 8; num[2][1] = 16; num[2][2] = 32; num[2][3] = 128;
num[3][0] = 16; num[3][1] = 32; num[3][2] = 64; num[3][3] = 128;
*/



